1.ECMAScript 的类型 #

2.函数调用 #

2.1 src\Reference.js #

src\Reference.js

const EnvironmentRecord = require('./EnvironmentRecord');
class Reference {
    constructor(base, name, strict) {
        this.base = base;
        this.name = name;
        this.strict = strict;
    }
    static GetBase(V) {
        return V.base;
    }
    static GetReferencedName(V) {
        return V.name;
    }
    static IsStrictReference(V) {
        return V.strict;
    }
    static HasPrimitiveBase(V) {
        return V.base.toString() === `[Object Boolean]`
            || V.base.toString() === `[Object String]`
            || V.base.toString() === `[Object Number]`
    }
    static IsPropertyReference(V) {
        return (typeof V.base === 'object' && (!(V.base instanceof EnvironmentRecord))) || Reference.HasPrimitiveBase(V)
    }
    static IsUnresolvableReference(V) {
        return typeof V.base === 'undefined';
    }
    static GetValue(V) {
        //如果 Type(V) 不是引用 , 返回 V。
        if (!Reference.Type(V)) return V;
        //令 base 为调用 GetBase(V) 的返回值
        let base = Reference.GetBase(V);
        //如果 IsUnresolvableReference(V), 抛出一个 ReferenceError 异常。
        if (Reference.IsUnresolvableReference(V)) {
            throw new Error(`ReferenceError ${V} is not defined`);
        }
        //如果 IsPropertyReference(V),
        if (Reference.IsPropertyReference(V)) {
            //如果 HasPrimitiveBase(V) 是 false,
            if (!Reference.HasPrimitiveBase(V)) {
                let name = Reference.GetReferencedName(V);
                return base[name];
            } else {
                let O = Reference.ToObject(base);
                let name = Reference.GetReferencedName(V);
                let desc = Object.getOwnPropertyDescriptor(O, name);
                if (typeof desc === 'undefined') return undefined;
                if (Reference.IsDataDescriptor(desc)) {
                    return desc.value;
                } else if (IsAccessorDescriptor(desc)) {
                    let getter = desc.get;
                    if (typeof getter === 'undefined') return undefined;
                    return getter.call(base);
                }
            }
        } else {
            //否则 , base 必须是一个 environment record
            //传递 GetReferencedName(V) 和 IsStrictReference(V) 为参数调用 base 的 GetBindingValue( 见 10.2.1) 具体方法,返回结果。
            let name = Reference.GetReferencedName(V);
            let strict = Reference.IsStrictReference(V);
            base.GetBindingValue(name, strict);
        }
        return base(Reference.GetReferencedName(V));
    }
    static IsDataDescriptor(desc) {
        return desc.value || desc.writable;
    }
    static IsAccessorDescriptor(desc) {
        return desc.get || desc.set;
    }
    static ToObject(PrimitiveValue) {
        let type = Object.prototype.toString.call(PrimitiveValue).slice(8, -1);
        switch (type) {
            case 'Boolean':
                return new Boolean(PrimitiveValue);;
            case 'Number':
                return new Number(PrimitiveValue);
            case 'String':
                return new String(PrimitiveValue);
            default:
                break;
        }
    }
    static Type(V) {
        return V instanceof Reference;
    }
}
module.exports = Reference;

2.2 this.js #

src\3.this.js

//console.log(obj.getName());
//令 ref 为解释执行 MemberExpression 的结果
var name = 1;
var obj = {
    name: 2,
    getName: function () {
        return this.name;
    }
}
console.log(obj.getName());
/*
console.log((obj.getName)());
console.log((obj.getName = obj.getName)());
console.log((true && obj.getName)());
console.log((obj.getName, obj.getName)());
console.log((true && obj.getName)()); */

const Reference = require('./Reference');
let ref = new Reference(obj, 'getName', false);
//let ref = obj.getName;
//令 func 为 GetValue(ref).
let func = Reference.GetValue(ref);
//令 argList 为解释执行 Arguments 的结果 , 产生参数值们的内部列表 (see 11.2.4).
let argList = [];
//如果 Type(func) is not Object ,抛出一个 TypeError 异常 .
/* if (!Type(func)) {
    throw new Error(`TypeError`);
} */
//如果 IsCallable(func) is false ,抛出一个 TypeError 异常 .
/* if (!IsCallable(func)) {
    throw new Error(`TypeError`);
} */
//如果 Type(ref) 为 Reference,那么 如果 IsPropertyReference(ref) 为 true,
//那么 令 thisValue 为 GetBase(ref).
//否则, ref 的基值是一个环境记录项 令 thisValue 为调用 GetBase(ref) 的 ImplicitThisValue 具体方法的结果
let thisValue;
if (Reference.Type(ref)) {
    if (Reference.IsPropertyReference(ref)) {
        thisValue = Reference.GetBase(ref);
    } else {
        thisValue = Reference.GetBase(ref).ImplicitThisValue();
    }
} else {
    //否则 , 假如 Type(ref) 不是 Reference. 令 thisValue 为 undefined.
    thisValue = undefined;
}
//返回调用 func 的 [[Call]] 内置方法的结果 , 传入 thisValue 作为 this 值和列表 argList 作为参数列表
let result = func.call(thisValue, ...argList);
console.log(result);

3.[[Call]] #

//当用一个 this 值,一个参数列表调用函数对象 F 的 [[Call]] 内部方法,采用以下步骤:
const ExecutionContext = require('./ExecutionContext');
const LexicalEnvironment = require('./LexicalEnvironment');
const FunctionDeclaration = require('./FunctionDeclaration');
const Reference = require('./Reference');
const ECStack = require('./ECStack');
function sum(a, b) {
    console.log(this, a, b);
}
//sum();
let fn = 'sum';
let FormalParameterList = "a,b";
let FunctionBody = `
   console.log(this, a, b);
`;
const globalLexicalEnvironment = LexicalEnvironment.NewObjectEnvironment(global, null);
const globalEC = new ExecutionContext(globalLexicalEnvironment, global);
ECStack.push(globalEC);
let env = ECStack.current.lexicalEnvironment.environmentRecord;
let Scope = ECStack.current.lexicalEnvironment;
let fo = FunctionDeclaration.createInstance(fn, FormalParameterList, FunctionBody, Scope);
let ref = new Reference(env, 'sum', false);
let thisValue;
if (Reference.Type(ref)) {
    if (Reference.IsPropertyReference(ref)) {
        thisValue = Reference.GetBase(ref);
    } else {
        thisValue = Reference.GetBase(ref).ImplicitThisValue();
    }
} else {
    //否则 , 假如 Type(ref) 不是 Reference. 令 thisValue 为 undefined.
    thisValue = undefined;
}
let FormalParameters = FormalParameterList.split(',');
let localEnv = LexicalEnvironment.NewDeclarativeEnvironment(fo[`[[Scope]]`]);
let funcCtx = new ExecutionContext(localEnv, thisValue);
ECStack.push(funcCtx);
env = ECStack.current.lexicalEnvironment.environmentRecord;
let strict = false;
let args = [1, 2];
//令 argCount 为 args 中元素的数量
let argCount = args.length;
let n = 0;
FormalParameters.forEach(argName => {
    n += 1;
    let v = n > argCount ? undefined : args[n - 1];
    argAlreadyDeclared = env.HasBinding(argName);
    if (!argAlreadyDeclared) {
        env.CreateMutableBinding(argName);
    }
    env.SetMutableBinding(argName, v, strict);
});
console.log(
    thisValue,
    Reference.GetValue(
        LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'a')),
    Reference.GetValue(
        LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'b'))
);
ECStack.pop();

4. call #

//Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )

function sum(a, b) {
    console.log(this, a, b);
}
let thisArg = { name: 'obj' };
sum.call(thisArg, 1, 2);
//当以 thisArg 和可选的 arg1, arg2 等等作为参数在一个 func 对象上调用 call 方法,采用如下步骤:
//如果 IsCallable(func) 是 false, 则抛出一个 TypeError 异常。
let func = sum;
/* if (!IsCallable(func)) {
    throw new Error(`TypeError`);
} */
//令 argList 为一个空列表。
let argList = [];
//如果调用这个方法的参数多余一个,则从 arg1 开始以从左到右的顺序将每个参数插入为 argList 的最后一个元素。
argList.push(1);
argList.push(2);
//提供 thisArg 作为 this 值并以 argList 作为参数列表,调用 func 的 [[Call]] 内部方法,返回结果。
console.log(
    thisArg
);

5. apply #

//Function.prototype.apply (thisArg, argArray)
const { Type } = require('./utils');
function func(a, b) {
    console.log(this, a, b);
}
let thisArg = { name: 'obj' };
func.apply(thisArg, [1, 2]);
//当以 thisArg 和 argArray 为参数在一个 func 对象上调用 apply 方法,采用如下步骤:
//如果 IsCallable(func) 是 false, 则抛出一个 TypeError 异常 .
/* if (!IsCallable(func)) {
    throw new Error(`TypeError`);
} */
//如果 argArray 是 null 或 undefined,
//则返回提供 thisArg 作为 this 值并以空参数列表调用 func 的 [[Call]] 内部方法的结果。
let argArray = [1, 2];
if (!argArray) {
    console.log(thisArg, undefined, undefined);
}
//如果 Type(argArray) 不是 Object, 则抛出一个 TypeError 异常 .
if (Type(argArray) !== 'object') {
    throw new Error(`TypeError`);
}
//令 len 为以 "length" 作为参数调用 argArray 的 [[Get]] 内部方法的结果。
let len = argArray.length;
//令 n 为 ToUint32(len).
let n = len;
//令 argList 为一个空列表 .
let argList = [];
let index = 0;
while (index < n) {
    let indexName = index.toString();
    let nextArg = argArray[indexName];
    argList.push(nextArg);
    index = index + 1;
}
//提供 thisArg 作为 this 值并以 argList 作为参数列表,调用 func 的 [[Call]] 内部方法,返回结果。

6. bind #

// Function.prototype.bind (thisArg [, arg1 [, arg2, …]])
// bind 方法需要一个或更多参数,thisArg 和(可选的)arg1, arg2, 等等,执行如下步骤返回一个新函数对象:
function func(a, b) {
    console.log(this, a, b);
}
let thisArg = { name: 'obj' };
let newFunc = func.bind(thisArg, 1);
newFunc(2);

const { IsCallable } = require('./utils');
//令 Target 为 this 值 .
let Target = thisArg;
//如果 IsCallable(Target) 是 false, 抛出一个 TypeError 异常 .
func[`[[call]]`] = true;
if (!IsCallable(func)) {
    throw new Error(`TypeError`);
}
//令 A 为一个(可能为空的)新内部列表,它包含按顺序的 thisArg 后面的所有参数(arg1, arg2 等等)。
let A = [1];
//令 F 为一个新原生 ECMAScript 对象。
let F = {};
//依照 8.12 指定,设定 F 的除了 [[Get]] 之外的所有内部方法。
//依照 15.3.5.4 指定,设定 F 的 [[Get]] 内部属性。
//设定 F 的 [[TargetFunction]] 内部属性为 Target。
F[`[[TargetFunction]]`] = Target;
//设定 F 的 [[BoundThis]] 内部属性为 thisArg 的值。
F[`[[BoundThis]]`] = thisArg;
//设定 F 的 [[BoundArgs]] 内部属性为 A。
F[`[[BoundArgs]]`] = A;
//设定 F 的 [[Class]] 内部属性为 "Function"。
F[`[[Class]]`] = 'Function';
//设定 F 的 [[Prototype]] 内部属性为 15.3.3.1 指定的标准内置 Function 的 prototype 对象。
F[`[[Prototype]]`] = Function.prototype;
//依照 15.3.4.5.1 描述,设定 F 的 [[Call]] 内置属性。

//依照 15.3.4.5.2 描述,设定 F 的 [[Construct]] 内置属性。

//依照 15.3.4.5.3 描述,设定 F 的 [[HasInstance]] 内置属性。

//如果 Target 的 [[Class]] 内部属性是 "Function", 则
//令 L 为 Target 的 length 属性减 A 的长度。
//设定 F 的 length 自身属性为 0 和 L 中更大的值。
if (Target[`[[Class]]`] === 'Function') {
    let L = Target.length - A.length;
    F.length = Math.max(0, L);
} else {
    //否则设定 F 的 length 自身属性为 0.
    F.length = 0;
}

//设定 F 的 length 自身属性的特性为 15.3.5.1 指定的值。
//设定 F 的 [[Extensible]] 内部属性为 true。
F[`[[Extensible]] `] = true;
//令 thrower 为 [[ThrowTypeError]] 函数对象 (13.2.3)。
let thrower = (key, value) => { };
//以 "caller", 属性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: - false}, 和 false 作为参数调用 F 的 [[DefineOwnProperty]] 内部方法。
Object.defineProperty(F, 'caller', {
    get: thrower,
    set: thrower,
    enumerable: false,
    configurable: false
});
//以 "arguments", 属性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, 和 false 作为参数调用 F 的 [[DefineOwnProperty]] 内部方法。
Object.defineProperty(F, 'arguments', {
    get: thrower,
    set: thrower,
    enumerable: false,
    configurable: false
});
//bind 方法的 length 属性是 1。
//Function.prototype.bind 创建的函数对象不包含 prototype 属性或 [[Code]], [[FormalParameters]], [[Scope]] 内部属性

// 当调用一个用 bind 函数创建的函数对象 F 的 [[Call]] 内部方法,传入一个 this 值和一个参数列表 ExtraArgs,采用如下步骤:
let boundArgs = F[`[[BoundArgs]]`];
let boundThis = F[`[[BoundThis]]`];
let target = F[`[[TargetFunction]]`];

//令 args 为一个新列表,它包含与列表 boundArgs 相同顺序相同值,后面跟着与 ExtraArgs 是相同顺序相同值。
let ExtraArgs = [2];
let args = [...boundArgs, ...ExtraArgs];
//提供 boundThis 作为 this 值,提供 args 为参数调用 target 的 [[Call]] 内部方法,返回结果。
console.log(
    boundThis,
    ...args
);

7. call/apply/bind #

!function (proto) {
    function getContext(context) {
        context = context || window;
        var type = typeof context;
        if (['number', 'string', 'boolean', 'null'].includes(type)) {
            context = new context.constructor(context);
        }
        return context;
    }
    function call(context, ...args) {
        context = getContext(context);
        context._fn = this;
        let result = context._fn(...args);
        delete context._fn;
        return result;
    }
    function apply(context, args) {
        context = getContext(context);
        context._fn = this;
        let result = context._fn(...args);
        delete context._fn;
        return result;
    }

    function bind(context, ...bindArgs) {
        return (...args) => this.call(context, ...bindArgs, ...args);
    }
    proto.call = call;
    proto.apply = apply;
    proto.bind = bind;
}(Function.prototype)

8.7 引用规范类型 #

var a = 1;
var aReference = {
    base: environmentRecords,
    name: 'a',
    strict: false
}

var obj = {
    name: 'zhufeng',
    getName() {
        console.log(this.name);
    }
}
var getNameReference = {
    base: obj,
    name: 'getName',
    strict: false
};

function two() {
    console.log(this);
}
two();
var twoReference = {
    base: environmentRecords,
    name: 'two',
    strict: false
};
方法 含义
GetBase(V) 返回引用值 V 的基值组件
GetReferencedName(V) 返回引用值 V 的引用名称组件
IsStrictReference(V) 返回引用值 V 的严格引用组件
HasPrimitiveBase(V) 如果基值是 Boolean, String, Number,那么返回 true
IsPropertyReference(V) 如果基值是个对象或 HasPrimitiveBase(V) 是 true,那么返回 true;否则返回 false
IsUnresolvableReference(V) 如果基值是 undefined 那么返回 true,否则返回 false

8.7.1 GetValue(v) #

8.7.2 PutValue(v,w) #

8.10.1 IsAccessorDescriptor (Desc) #

8.10.2 IsDataDescriptor (Desc) #

8.12.3 [[Get]] (P) #

当用属性名 P 调用 O 的 [[Get]] 内部方法,采用以下步骤:

9.9 ToObject #

输入类型 结果
Undefined 抛出 TypeError 异常。
Null 抛出 TypeError 异常。
Boolean 创建一个新的Boolean对象,其 [[PrimitiveValue]]属性被设为该布尔值的值。
Number 创建一个新的Number对象,其[[PrimitiveValue]]属性被设为该数字值。
String 创建一个新的String对象,其 [[PrimitiveValue]] 属性被设为该字符串值。
Object 结果是输入的参数(不转换)。

10.3.1 标识符解析 #

11.1.6 分组表达式 #

11.13.1 简单赋值 #

11.2.3 函数调用 #

11.4.3 typeof 运算符 #

val类型 结果
Undefined "undefined"
Null "null"
Boolean "boolean"
Number "number"
String "string"
Object(原生,且没有实现 [[call]]) "object"
Object(原生或者宿主且实现了 [[call]] "function"
Object(宿主且没实现 [[call]]) 由实现定义,但不能是 "undefined", "boolean", "number", or "string"

11.11 二元逻辑运算符 #

11.14 逗号运算符 #

13.2.1 [[call]] #

15.3.4.3 Function.prototype.apply (thisArg, argArray) #

在外面传入的 thisArg 值会修改并成为 this 值。thisArg 是 undefined 或 null 时它会被替换成全局对象,所有其他值会被应用 ToObject 并将结果作为 this 值,这是第三版引入的更改。

15.3.4.4 Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] ) #
15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, …]]) #

Function.prototype.bind 创建的函数对象不包含 prototype 属性或 [[Code]], [[FormalParameters]], [[Scope]] 内部属性

15.3.4.5.1 [[Call]] #

3.面试题 #

3.1 第1题 #

var name = "window";
var obj = {
    name: "hello",
    getName() {
        console.log(this.name);
    }
};
function getName() {
    var objGetName = obj.getName;
    objGetName();//window
    obj.getName();//hello
    (obj.getName)();//hello
    (true && obj.getName)();//window
}
getName();

3.2 第2题 #

var name = 'window';
var obj = {
    name: 'obj',
    getName1() {
        console.log(this.name)
    },
    getName2: () => console.log(this.name),
    getName3() {
        return function () {
            console.log(this.name)
        }
    },
    getName4() {
        return () => {
            console.log(this.name)
        }
    }
}
var obj2 = { name: 'obj2' }
obj.getName1();//obj
obj.getName1.call(obj2);//obj2
obj.getName2();//window
obj.getName2.call(obj2);//window
obj.getName3()();//window
obj.getName3.call(obj2)();//window
obj.getName3().call(obj2);//obj2
obj.getName4()();//obj
obj.getName4.call(obj2)();//obj2
obj.getName4().call(obj2);//obj

qfct